/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2017 All Rights Reserved.
 */
package com.citytsm.gateway.sdk.utils;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 *
 * @author James
 * @version $Id: BeanUtil.java, v 0.1 2017年07月02日 上午8:57 James Exp $
 */
public class BeanUtil {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    public static String convertToString(Object obj) throws JsonProcessingException {
        return objectMapper.writeValueAsString(obj);
    }

    public static <T> T convertToBean(Object obj, Class<T> clazz) throws IOException {
        String json = objectMapper.writeValueAsString(obj);
        return objectMapper.readValue(json, clazz);
    }

    public static <T> T convertJsonToBean(String json, Class<T> clazz) throws IOException {
        return objectMapper.readValue(json, clazz);
    }

    public static Map<String, String> convertToMap(Object obj)
            throws JsonProcessingException, InvocationTargetException, IllegalAccessException {
        return convertToMap(obj, false);
    }

    /**
     * 将bean转换成为map，value为field的toString()结果。
     * bean需要为单层结构，并有get方法。
     * @param obj
     * @return
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    public static Map<String, String> convertToMap(Object obj, boolean toJsonIfNotBasic)
            throws JsonProcessingException, InvocationTargetException, IllegalAccessException {
        Map<String, String> map = new HashMap<>();

        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(obj.getClass());
        for (PropertyDescriptor property : propertyDescriptors) {

            String key = property.getName();
                // 得到property对应的getter方法
            Method getter = property.getReadMethod();
            if (getter != null) {
                Object value = getter.invoke(obj);
                if (value != null) {
                    if (isBasicType(property.getPropertyType())) {
                        map.put(key, value.toString());
                    } else if (toJsonIfNotBasic) {
                        map.put(key, objectMapper.writeValueAsString(value));
                    }
                }
            }
        }

        return map;
    }

    public static Map<String, String> convertToMapWithAnnotation(Object obj, boolean toJsonIfNotBasic){
        Map<String, String> map = new HashMap<>();
        List<Field> fieldList = Arrays.asList(obj.getClass().getDeclaredFields());
        for(Field field : fieldList){
            String key = null;
            if (!field.isAnnotationPresent(JsonIgnore.class)) {
                if (field.isAnnotationPresent(JsonProperty.class)) {
                    JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
                    String propertyKey = jsonProperty.value();
                    if (StringUtils.isNotBlank(propertyKey)) {
                        key = propertyKey;
                    }
                } else {
                    key = field.getName();
                }
                String value = getValueByProperty(obj, field.getName(), toJsonIfNotBasic);
                if (StringUtils.isNotEmpty(value)) {
                    map.put(key, value);
                }
            }
        }
        return map;
    }

    private static String getValueByProperty(Object obj, String fieldName, boolean toJsonIfNotBasic) {
        PropertyDescriptor property = BeanUtils.getPropertyDescriptor(obj.getClass(), fieldName);
        if (property != null) {
            Method getter = property.getReadMethod();
            if (getter != null) {
                try {
                    Object value = getter.invoke(obj);
                    if (value != null) {
                        if (isBasicType(property.getPropertyType())) {
                            return value.toString();
                        } else if (toJsonIfNotBasic) {
                            return objectMapper.writeValueAsString(value);
                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } else {
                throw new RuntimeException("字段不存在get方法");
            }
        }
        return null;
    }
    /**
     * bean转Map类型转换支持的类型
     * @param type
     * @param <T>
     * @return
     */
    private static <T> boolean isBasicType(Class<T> type) {
        return (type == String.class
                || type == Integer.class
                || type == BigDecimal.class // 注意有精度问题
                || type == Long.class
                || type == Double.class
                || type == Boolean.class
                || type == Float.class
                || type == Short.class);
    }


    public static <T> T assignFromMap(T obj, Map<String, String> properties) {
        if (obj == null || properties == null || properties.isEmpty()) {
            return obj;
        }

        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(obj.getClass());
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();

                // 得到property对应的getter方法
            Method setter = property.getWriteMethod();

            if (setter != null && properties.containsKey(key) && isBasicType(property.getPropertyType())) {
                try {
                    setter.invoke(obj, properties.get(key));
                //TODO
                } catch (IllegalAccessException e) {
                    //e.printStackTrace();
                } catch (InvocationTargetException e) {
                    //e.printStackTrace();
                }
            }
        }

        return obj;
    }

}